import { Macro } from '../../../script/Main/Macros';
import { Config, ViewZOrder } from '../../../script/config/Config';
import c3d from '../../../script/tool/cc';

/**
 *  提示
 */

class ToastItem extends c3d.Component {
    private _content: c3d.Node = null!;
    private _curPositon = new c3d.Vec3;
    private _curOpacity: c3d.UIOpacity | null = null;
    private _transform: c3d.UITransform = null!;

    stopAllActions() {
        c3d.Tween.stopAllByTarget(this._content);
        c3d.Tween.stopAllByTarget(this._curPositon);
        if (this.opacity) {
            c3d.Tween.stopAllByTarget(this.opacity);
        }
    }

    onDestroy() {
        this.stopAllActions();
    }

    init(content: string, time: number) {
        this._content = c3d.find("content", this.node) as c3d.Node;
        if (this._content) {
            (this._content.getComponent(c3d.Label) as c3d.Label).string = content;
        }
        this.runTimeOut(time);
    }

    private get opacity() {
        if (this._curOpacity) {
            return this._curOpacity;
        }
        this._curOpacity = this.node.getComponent(c3d.UIOpacity);
        return this._curOpacity;
    }

    private get transform() {
        if (this._transform) {
            return this._transform;
        }
        this._transform = this.node.getComponent(c3d.UITransform) as c3d.UITransform;
        return this._transform;
    }

    private runTimeOut(time: number) {
        let self = this;
        c3d.tween(this._content).delay(time).call(() => {
            GM.uiMgr.tip.finishShowItem(self.node);
        }).start();
    }

    public fadeOut() {
        if (!this.opacity) return;
        c3d.Tween.stopAllByTarget(this.opacity);
        c3d.tween(this.opacity)
            .to(.5, { opacity: 0 })
            .call(() => {
                this.stopAllActions();
                this.node?.removeFromParent();
            })
            .start();
        this.moveTo(0, this.node.position.y + this.transform.height);
    }

    public fadeIn() {
        if (!this.opacity) return;
        c3d.Tween.stopAllByTarget(this.opacity);
        this.opacity.opacity = 0;
        c3d.tween(this.opacity)
            .to(.5, { opacity: 255 })
            .start();
        this.moveTo(0, this.node.position.y + this.transform.height);
    }

    public moveTo(x: number, y: number) {
        c3d.Tween.stopAllByTarget(this._curPositon);
        this._curPositon.set(this.node.position);
        c3d.tween(this._curPositon).to(0.5, { x: x, y: y }, {
            onUpdate: (target) => {
                this.node?.setPosition(target as c3d.Vec3);
            }, easing: "expoOut"
        }).start();
    }
}

export default class Tips {

    private _prefab: c3d.Prefab = null!;

    private _queue: c3d.Node[] = [];

    private readonly MAX_NUM = 3; // 最多可以同时显示多少个toast
    private readonly FADE_TIME = 2; // 停留显示2秒。2秒内有可能被顶掉

    /** id*/
    private _id: number = 0;

    /** 默认的显示开始位置 */
    public startPosition = new c3d.Vec3(0, 100);

    public preloadPrefab() {
        this.loadPrefab();
    }
    private async loadPrefab() {
        return new Promise<boolean>((resolve, reject) => {
            if (this._prefab) {
                resolve(true);
                return;
            } else {
                GM.assetMgr.load(
                    Macro.BUNDLE_RESOURCES,
                    Config.CommonPrefabs.tips,
                    c3d.Prefab,
                    (finish, total, item) => { },
                    (data) => {
                        if (data && data.data && data.data instanceof c3d.Prefab) {
                            GM.assetMgr.addPersistAsset(Config.CommonPrefabs.tips, data.data, Macro.BUNDLE_RESOURCES);
                            this._prefab = data.data;
                            resolve(true);
                        } else {
                            resolve(false);
                        }
                    }
                );
            }
        });
    }

    private async _show(msg: string) {
        let finish = await this.loadPrefab();
        if (finish) {
            let node = c3d.instantiate(this._prefab);
            if (node) {
                let itemComp = node.addComponent(ToastItem);
                itemComp.init(msg, this.FADE_TIME);
                node.setPosition(this.startPosition);
                itemComp.fadeIn();
                node.setUserData<number>(this._id++);
                node.name = `Tips${node.getUserData<number>()}`;
                GM.uiMgr.addView(node, ViewZOrder.Tips);

                //整体上移
                let length = this._queue.length;
                for (let i = 0; i < length && i < this.MAX_NUM; i++) {
                    let item = this._queue[i];
                    let itemComp = item.getComponent(ToastItem);
                    let transform = item.getComponent(c3d.UITransform);
                    if (itemComp && transform) {
                        itemComp.moveTo(0, this.startPosition.y + transform.height + (length - i) * (transform.height + 3))
                    }
                }

                //压入
                this._queue.push(node);

                //删除超出的
                if (this._queue.length > this.MAX_NUM) {
                    let item = this._queue.shift() as c3d.Node;
                    item.getComponent(ToastItem)?.fadeOut();
                }
            }
        }
    }

    public show(msg: string) {
        if (msg == null || msg == undefined || msg == "") {
            return;
        }
        c3d.log("Toast.show msg=%s", msg);
        this._show(msg);
    }

    public finishShowItem(item: c3d.Node) {
        for (let i = 0; i < this._queue.length; i++) {
            let tempItem = this._queue[i];
            if (tempItem.getUserData<number>() == item.getUserData<number>()) {
                this._queue.splice(i, 1);
                item.getComponent(ToastItem)?.fadeOut();
                break;
            }
        }
    }

    public clear() {
        let item: c3d.Node = null!;
        while (item = this._queue.pop() as c3d.Node) {
            let comp = item.getComponent(ToastItem);
            if (comp) {
                comp.stopAllActions();
            }
            item.removeFromParent();
        }
    }

}